﻿/**
 * Created by xjchen on 2020-03-03.
 * */

#ifndef MIDDLEWARE_TASKMODULE_H
#define MIDDLEWARE_TASKMODULE_H


#include "BaseObject.h"
#include "Application.h"
#include "DeviceReader.h"
#include "DeviceWriter.h"
#include <stddef.h>
#include <typeinfo>
#include <algorithm>

#if defined(_WIN32) || defined(_WIN64)
#define TYPE_ID_NAME(inst)  typeid(inst).name()
#define OFFSET_OF(inst, m) (&reinterpret_cast<char const volatile&>(inst.m)-&reinterpret_cast<char const volatile&>(inst))
#else
#define TYPE_ID_NAME(inst)  typeid(typeof(inst)).name()
#define OFFSET_OF(inst, m)  offsetof(typeof(inst),m)
#endif

extern void
registerStructMember(std::string structName, std::string memberName, std::string typeIdName, int offset, int size);

#define REGISTER_STRUCT_MEMBER(owner, a) do{auto inst = owner;\
registerStructMember(TYPE_ID_NAME(inst),#a,TYPE_ID_NAME(inst.a),OFFSET_OF(inst,a),sizeof(inst.a));}while(0)
#define register_struct_and_members(structType, ...) EX_FOR_EACH(structType{},REGISTER_STRUCT_MEMBER,__VA_ARGS__)

#define REGISTER_PRIVATE_PARAMETER(a) do{registerPrivateParameter(STRINGIFY(a),typeid(a).name(), &a, sizeof(a));}while(0)
#define register_private_parameters(...)  FOR_EACH(REGISTER_PRIVATE_PARAMETER, __VA_ARGS__)

typedef struct buffer_node {
    bool valid;
    int64_t len;
    char *data;
    struct buffer_node *next;
} buffer_node_t;

typedef struct buffer_table {
    int count;
    buffer_node_t **list;
} buffer_table_t;

typedef struct param_info {
    bool is_struct;
    std::string name;
    std::string type;
    void *addr;
    int size;
} param_info_t;

enum LaunchMode {
    /* 只有所有输入端口有数据时才启动，默认 */
    LaunchMode_AND,
    /* 只要任意一个或多个输入端口有数据时即可启动 */
    LaunchMode_OR
};

struct LaunchCondition {
    /* 只有after模块计算完以后，才能启动，用以更精确的时序控制 */
    TaskModule *after;

    /* 第一次是否忽略 ，如果after是下游模块，则必须为true*/
    bool ignoreFirstTime;

    LaunchCondition() {
        this->after = nullptr;
        this->ignoreFirstTime = false;
    }

    LaunchCondition(TaskModule *pModule, bool ignoreFirstTime) {
        this->after = pModule;
        this->ignoreFirstTime = ignoreFirstTime;
    }
};

class TaskModuleLocal;

class TaskModule : public BaseObject {
public:

    TaskModule();

    ~TaskModule();

    virtual void onCompute(buffer_table_t *input, buffer_table_t *output) = 0;

    /**
     * 中断处理动作
     * 调用SoftInterrupt::interrupt()方法时响应的中断处理动作，子类应重载以实现不同行为
     */
    virtual void onInterrupted(void *extraInfo, int64_t infoLen);

    FLOW_API void addOutput(int thizIndex, int thatIndex, TaskModule *pModule, int64_t dataLen);

    FLOW_API void addDeviceWriter(int index, DeviceWriter *writer, int64_t dataLen);

    /** 最大积累数,默认1 */
    FLOW_API void setMaxAccumulationCount(int count);

    /* 设置启动策略 */
    FLOW_API void setLaunchPolicy(LaunchMode mode, LaunchCondition condition);

    /*
     *  默认是双buffer(乒乓策略),可以设置更大的buffer池用于缓存，或者单buffer用以节省内存
     *  注：这里的buffer仅指输出端口的buffer，输入端口的buffer只是上游模块的指针引用，应由上游模块设置
     *  */
    FLOW_API void setCacheBufferPool(int numBuffers);

    /**
     * 将任务绑定在某个CPU核上运行
     * 对于NUMA设备，请在创建对象后立即调用此API，因为在其他API中实现的NUMA相关内存分配策略
     * 依据为此处设定的CPU核
     *
     * @param whichCore 绑定的CPU核
     */
    FLOW_API void bindCPUCore(int coreId);

    /**
     * 1. 指定当前输出端口的数据长度；index 输出端口号；length 本次写入的数据长度，不得大于该端口的默认长度。
     * 2. 如果是积累，下一步积累将直接拼接，该端口buffer_node_t的data字段对应相应的偏移地址，len字段对应剩余的buffer长度。
     * 3. 如果length为0，相当于屏蔽该端口，下一模块的对应输入端口将以空数据驱动。
     * */
    USER_API void setCurrentOutputLength(int index, int64_t length);

    /**
     * 结束积累，直接输出，数据长度会变短。
     * 如果模块连接DeviceWriter，慎用，因为下游设备所对应的DeviceReader不知道数据变短了。
     * */
    USER_API void breakAccumulation();

    /** 得到已经积累的次数 */
    USER_API int getAccumStep();

    /** 是否是最后一次积累 */
    USER_API bool isLastAccumStep();

    /**
     *  1. 动态屏蔽输出端口并以空数据向下驱动；端口对应bit(从右向左)置0，则该端口被屏蔽。每次onCompute前，屏蔽会自动清除。
     *  2. 端口屏蔽后，该端口的下游分支的所有模块如果与其它分支无交叉，不再运行；如果有交叉，则对应端口置空数据后，
     *     仍配合其它分支的有效数据，驱动模块运行。计算时，可根据buffer_node_t的valid字段判断是否为空数据。
     *  4. 要屏蔽的端口尽量不要连接DeviceWriter，因为下游设备所对应的DeviceReader不知道数据是否无效或变短了。
     *  */
    USER_API void maskOutput(int mask);

    /**
    *  1. 动态屏蔽输出端口，不再向下驱动；端口对应bit(从右向左)置0，则该端口被屏蔽。每次onCompute前，屏蔽会自动清除。
    *  2. 应用场景：依次来的数据及时分发给不同模块并行计算，然后合并处理。尽管先通过积累，然后分发也可实现，但造成延时。
    *  */
    USER_API void maskOutputWithoutDrive(int mask);

    /*
     * 获取指定输入端口的数据批次
     * 对于LaunchMode_OR驱动策略，各个输入端口的数据批次可能不一样
     */
    USER_API uint64_t getInputSequence(int inputIndex);

    /*
    * 获取指定输出端口的数据批次
    * 当maskOutputWithoutDrive时，各个输出端口的数据批次可能不一样。注：空数据仍会当作一个数据批次。
    */
    USER_API uint64_t getOutputSequence(int outputIndex);

    /* 启动计算的批次 */
    int64_t inputSequence = 0L;
    /* 向下驱动的批次 */
    int64_t outputSequence = 0L;

    TaskModuleLocal *local;
protected:
    void registerPrivateParameter(std::string name, std::string type, void *addr, int size);
};

#endif /** MIDDLEWARE_TASKMODULE_H */
